﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary.Statistics.Distribution
{
    /// <summary>
    /// This class represents a Pareto distribution.
    /// </summary>
    [Serializable]
    public class ParetoDistribution
    {
        /// <summary>
        /// The scale parameter of the distribution.
        /// </summary>
        private readonly double scale;

        /// <summary>
        /// The shape parameter of the distribution.
        /// </summary>
        private readonly double shape;

        /// <summary>
        /// Initializes a new instance of the <see cref="ParetoDistribution"/> class.
        /// </summary>
        /// <param name="shape">The shape parameter of the distribution.</param>
        /// <param name="scale">The scale parameter of the distribution.</param>
        public ParetoDistribution(double shape, double scale)
        {
            if (scale <= 0)
            {
                throw new ArgumentException("scale <= 0");
            }

            if (shape <= 0)
            {
                throw new ArgumentException("shape <= 0");
            }

            this.scale = scale;
            this.shape = shape;
        }

        /// <summary>
        /// Gets the scale parameter of the distribution.
        /// </summary>
        /// <value>The scale parameter of the distribution.</value>
        public double Scale
        {
            get { return this.scale; }
        }

        /// <summary>
        /// Gets the shape parameter of the distribution.
        /// </summary>
        /// <value>The shape parameter of the distribution.</value>
        public double Shape
        {
            get { return this.shape; }
        }

        /// <summary>
        /// Gets the mean of the distribution.
        /// </summary>
        /// <value>The mean of the distribution.</value>
        public double Mean
        {
            get
            {
                if (this.shape > 1)
                {
                    return (this.shape*this.scale)/(this.shape - 1);
                }

                return 0;
            }
        }

        /// <summary>
        /// Gets the median of the distribution.
        /// </summary>
        /// <value>The median of the distribution.</value>
        public double Median
        {
            get { return this.scale*Math.Pow(2, 1/this.shape); }
        }

        /// <summary>
        /// Gets the mode of the distribution.
        /// </summary>
        /// <value>The mode of the distribution.</value>
        public double Mode
        {
            get { return this.scale; }
        }

        /// <summary>
        /// Gets the variance of the distribution.
        /// </summary>
        /// <value>The variance of the distribution.</value>
        public double Variance
        {
            get
            {
                if (this.shape > 2)
                {
                    return (this.scale*this.scale*this.shape)/(Math.Pow(this.shape - 1, 2)*this.shape - 2);
                }

                return 0;
            }
        }

        /// <summary>
        /// Gets the skewness of the distribution.
        /// </summary>
        /// <value>The skewness of the distribution.</value>
        public double Skewness
        {
            get
            {
                if (this.shape > 3)
                {
                    return ((2*(1 + this.shape))/(this.shape - 3))*Math.Sqrt((this.shape - 2)/this.shape);
                }

                return 0;
            }
        }

        /// <summary>
        /// Computes the probability mass function of the Pareto distribution at the specified postion x.
        /// </summary>
        /// <param name="x">The specified position x.</param>
        /// <returns>The probability mass function of the Pareto distribution at the specified postion x.</returns>
        public double ProbabilityMassFunction(double x)
        {
            if (x > this.scale)
            {
                return (this.shape*Math.Pow(this.scale, this.shape))/(Math.Pow(x, this.shape + 1));
            }

            return 0;
        }

        /// <summary>
        /// Computes the cumulative distribution function of the Pareto distribution at the specified postion x.
        /// </summary>
        /// <param name="x">The specified position x.</param>
        /// <returns>The cumulative distribution function of the Pareto distribution at the specified postion x.</returns>
        public double CumulativeDistributionFunction(double x)
        {
            return 1 - Math.Pow(this.scale/x, this.shape);
        }
    }
}